package cn.jeasy.core.test;


import java.util.List;

import org.junit.Rule;
import org.junit.internal.runners.model.ReflectiveCallable;
import org.junit.rules.RunRules;
import org.junit.rules.TestRule;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;

import cn.jeasy.core.context.ApplicationContext;
import cn.jeasy.core.context.ClassPathXmlApplicationContext;
import cn.jeasy.core.context.annotation.ContextConfiguration;


public class JeasyJUnit4ClassRunner extends BlockJUnit4ClassRunner {
     private ApplicationContext applicationContext;
	
	 private  Object test;

	public JeasyJUnit4ClassRunner(Class<?> klass) throws InitializationError {
		super(klass);
	}
	 // 拦截 BeforeClass 事件
    protected Statement withBeforeClasses(final Statement statement) {
        final Statement junitStatement = super.withBeforeClasses(statement);
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                System.out.println("Before Class: " + getTestClass().getName());
                initContext();
                junitStatement.evaluate();
            }
 
        };
    }
    /**
     * 初始化容器
     */
    private void initContext(){
		try {	
			String contextConfigLocation="applicationContext.xml";
			
			if(getTestClass().getJavaClass().isAnnotationPresent(ContextConfiguration.class)){
				ContextConfiguration contextFile=this.getTestClass().getJavaClass().getAnnotation(ContextConfiguration.class);
				contextConfigLocation=contextFile.value()[0];
			}
			test = new ReflectiveCallable() {
                @Override
                protected Object runReflectiveCall() throws Throwable {
                    return createTest();
                }
            }.run();
			 applicationContext=new ClassPathXmlApplicationContext(contextConfigLocation);
			 applicationContext.autowiredPropertyValues(test);
		} catch (Exception e) {		
			e.printStackTrace();
		} catch (Throwable e) {			
			e.printStackTrace();
		}
	}
    @SuppressWarnings("deprecation")
	protected Statement methodBlock(FrameworkMethod method) {
        Statement statement = methodInvoker(method, test);
        statement = possiblyExpectingExceptions(method, test, statement);
        statement = withPotentialTimeout(method, test, statement);
        statement = withBefores(method, test, statement);
        statement = withAfters(method, test, statement);
        statement = withRules(method, test, statement);
        return statement;
    }
    private Statement withRules(FrameworkMethod method, Object target,
            Statement statement) {
        List<TestRule> testRules = getTestRules(target);
        Statement result = statement;
        result = withMethodRules(method, testRules, target, result);
        result = withTestRules(method, testRules, result);

        return result;
    }
    private Statement withMethodRules(FrameworkMethod method, List<TestRule> testRules,
            Object target, Statement result) {
        for (org.junit.rules.MethodRule each : getMethodRules(target)) {
            if (!testRules.contains(each)) {
                result = each.apply(result, method, target);
            }
        }
        return result;
    }

    private List<org.junit.rules.MethodRule> getMethodRules(Object target) {
        return rules(target);
    }

    /**
     * @param target the test case instance
     * @return a list of MethodRules that should be applied when executing this
     *         test
     */
    protected List<org.junit.rules.MethodRule> rules(Object target) {
        return getTestClass().getAnnotatedFieldValues(target, Rule.class,
                org.junit.rules.MethodRule.class);
    }

    /**
     * Returns a {@link Statement}: apply all non-static {@link Value} fields
     * annotated with {@link Rule}.
     *
     * @param statement The base statement
     * @return a RunRules statement if any class-level {@link Rule}s are
     *         found, or the base statement
     */
    private Statement withTestRules(FrameworkMethod method, List<TestRule> testRules,
            Statement statement) {
        return testRules.isEmpty() ? statement :
                new RunRules(statement, testRules, describeChild(method));
    }

    /**
     * @param target the test case instance
     * @return a list of TestRules that should be applied when executing this
     *         test
     */
    protected List<TestRule> getTestRules(Object target) {
        List<TestRule> result = getTestClass().getAnnotatedMethodValues(target,
                Rule.class, TestRule.class);

        result.addAll(getTestClass().getAnnotatedFieldValues(target,
                Rule.class, TestRule.class));

        return result;
    }

}
